# -*- coding: utf-8 -*-
import numpy as np

import geatpy as ea


class CF2(ea.Problem):  # 继承Problem父类

    def __init__(self, M=None, Dim=10):  # M : 目标维数；Dim : 决策变量维数
        name = 'CF2'  # 初始化name（函数名称，可以随意设置）
        M = 2  # 初始化M（目标维数）
        maxormins = [1] * M  # 初始化maxormins（目标最小最大化标记列表，1：最小化该目标；-1：最大化该目标）
        varTypes = [0] * Dim  # 初始化varTypes（决策变量的类型，0：实数；1：整数）
        lb = [0] + [-1] * (Dim - 1)  # 决策变量下界
        ub = [1] * Dim  # 决策变量上界
        lbin = [1] * Dim  # 决策变量下边界（0表示不包含该变量的下边界，1表示包含）
        ubin = [1] * Dim  # 决策变量上边界（0表示不包含该变量的上边界，1表示包含）
        # 调用父类构造方法完成实例化
        ea.Problem.__init__(self,
                            name,
                            M,
                            maxormins,
                            Dim,
                            varTypes,
                            lb,
                            ub,
                            lbin,
                            ubin)

    def evalVars(self, Vars):  # 目标函数
        x1 = Vars[:, [0]]
        J1 = np.arange(2, self.Dim, 2)
        J2 = np.arange(1, self.Dim, 2)
        tmp = 6 * np.pi * x1
        f1 = x1 + 2 * np.mean(
            (Vars[:, J1] - np.sin(tmp + np.pi / self.Dim * (J1 + 1)))**2,
            1,
            keepdims=True)
        f2 = 1 - np.sqrt(np.abs(x1)) + 2 * np.mean(
            (Vars[:, J2] - np.cos(tmp + np.pi / self.Dim * (J2 + 1)))**2,
            1,
            keepdims=True)
        ObjV = np.hstack([f1, f2])
        tmp = np.sqrt(np.abs(f1))
        tmp = f2 + tmp - np.sin(2 * np.pi * (tmp - f2 + 1)) - 1
        CV = -tmp / (1 + np.exp(4 * np.abs(tmp)))
        return ObjV, CV

    def calReferObjV(self):  # 设定目标数参考值（本问题目标函数参考值设定为理论最优值，即“真实帕累托前沿点”）
        N = 10000  # 生成10000个参考点
        ObjV1 = np.linspace(0, 1, N)
        ObjV2 = 1 - np.sqrt(ObjV1)
        referenceObjV = np.array([ObjV1, ObjV2]).T
        deleteIdx = np.where((0 < ObjV1) & (ObjV1 < 1 / 16)
                             | (1 / 4 < ObjV1) & (ObjV1 < 9 / 16))[0]
        return np.delete(referenceObjV, deleteIdx, 0)
